# module name base.py
# $Id: base.py ddcad87fab9c 2013/08/17 19:20:30 pgurumur $
# Copyright (c) 2013 Prabhu Gurumurthy <pgurumur@gmail.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# File base.py generated on Sun, July 21, 2013 15:57:53 PM
#
#
from __future__ import division

__version__  = "$Revision: ddcad87fab9c $"
__author__   = "$Author: pgurumur $"
__modified__ = "$Date: 2013-08-17 19:20:30Z $"

import heapq
import itertools
import operator

from collections import OrderedDict

try:
   from . import exception, stdlib
except ImportError:
   import exception, stdlib

builtins = set()
keywords = set()

def beryl_bool(function):
   def wrapper(*args, **kwargs):
      retval = function(*args, **kwargs)
      if retval == True:
         retval = BerylTrue()
      elif retval == False:
         retval = BerylFalse()

      return retval
   return wrapper

def kw_validate(Name = None):
   retval = BerylFalse()
   if Name and Name in keywords:
      retval = BerylTrue()

   return retval

def kw_list():
   return list(keywords)

def kw_add(String = None):
   keywords.add(String)

class InvalidOperand(object):
   def __init__(self, *args):
      self._args = args

   def __call__(self, function):
      def wrapper(*args, **kwargs):
         if isinstance(args[1], self._args):
            raise exception.InternalError(
                  "invalid operand variable type %s" %type(args[1]))

         return function(*args, **kwargs)

      return wrapper

class ObjectAdd(object):
   def __init__(self, clstype):
      self._clstype = clstype

   def __call__(self, function):
      def wrapper(*args, **kwargs):
         kls = args[0]
         obj = args[1]
         cls = None

         if self._clstype == "function":
            cls = BerylFunction
         elif self._clstype == "namespace":
            cls = BerylNamespace
         elif self._clstype == "variable":
            cls = BerylVariable
         else:
            raise exception.InternalError("invalid class type")

         if obj is not None and isinstance(obj, cls):
            function(*args, **kwargs)
            if obj.parent is None:
               obj.parent = kls
         else:
            raise exception.InternalError("invalid object to add")

      return wrapper

class BerylTask(object):
   def __init__(self):
      super(BerylTask, self).__init__()
      self._pq = []
      self._entry = {}
      self._counter = itertools.count()
      self._removed = "<>"

   def add(self, task, priority = 0):
      if task:
         if task in self._entry:
            self.remove(task)

         count = next(self._counter)
         entry = [priority, count, task]
         self._entry[task] = entry
         heapq.heappush(self._pq, entry)

   def remove(self, task):
      if task:
         entry = self._entry.pop(task)
         entry[-1] = self._removed

   def pop(self):
      retval = None
      while self._pq:
         priority, count, task = heapq.heappop(self._pq)
         if task is not self._removed:
            del self._entry[task]
            retval = task
            break

      if retval is None:
         raise KeyError("priority queue is empty")

      return retval

   def __add__(self, task):
      self.add(task, 0)
      return self

   def __sub__(self, task):
      self.remove(task)
      return self

   def __len__(self):
      return len(self._pq)

class BerylBase(object):
   def __init__(self):
      super(BerylBase, self).__init__()
      self._linenum = 0
      self._name = None

   def __repr__(self):
      return str(self)

   def error(self, fmt, *args):
      errmesg = None
      if len(args):
         errmesg = stdlib.sprintf(fmt, *args)
      else:
         errmesg = str(fmt)

      raise exception.InternalError(errmesg)

   def __hash__(self):
      raise NotImplementedError()

   def GetLineNumber(self):
      return self._linenum

   def SetLineNumber(self, Value):
      try:
         self._linenum = int(Value)
      except ValueError:
         self.error("INTERNAL ERROR, cannot coerce value to integer")

   def SetName(self, Name = None):
      if Name:
         self._name = str(Name)

   def GetName(self):
      return self._name

   name = property(GetName, SetName)
   line = property(GetLineNumber, SetLineNumber)
   objects = property(fget = lambda self: self._objects)

class BerylObject(BerylBase):
   def __init__(self, Name):
      super(BerylObject, self).__init__()
      self._functions = OrderedDict()
      self._variable = OrderedDict()

      self._parent = None
      self._name = Name

   def __hash__(self):
      return hash(self._name)

   def __call__(self, *args, **kwargs):
      raise NotImplementedError()

   def SetParent(self, Object = None):
      if self._parent:
         self.error("parent is already set")

      if Object is None:
         self.error("invalid parent object")

      if isinstance(Object, BerylObject):
         self._parent = Object
      else:
         self.error("cannot add non BerylObjects as parent")

   def GetParent(self):
      return self._parent

   def GetName(self):
      return self._name

   def SetName(self, Name):
      if isinstance(Name, str):
         self._name = Name
      else:
         self.error("variable name must be a string")

   @ObjectAdd("variable")
   def AddVariable(self, Object = None):
      self._variable[Object.name] = Object

   def Add(self, Object):
      if isinstance(Object, BerylVariable):
         self.AddVariable(Object)
      elif isinstance(Object, BerylFunction):
         self.AddFunction(Object)
      elif isinstance(Object, BerylNamespace):
         self.AddNamespace(Object)

   def FindVar(self, Name = None):
      return self.__find(Name, "variable")

   def __find(self, Name = None, ftype = None):
      retval = None
      container = None
      if ftype.lower() == "variable":
         container = self._variable
      elif ftype.lower() == "function":
         container = self._function
      elif ftype.lower() == "namespace":
         container = self._ns

      for name in container.keys():
         if Name is not None and name == Name:
            retval = container[name]
            break

      if retval is None and str(self) != "__main__":
         retval = super(BerylNamespace, self).__find(Name, ftype)

      return retval

   name = property(GetName, SetName)
   parent = property(GetParent, SetParent)

class BerylNamespace(BerylObject):
   def __init__(self, Name = None):
      super(BerylNamespace, self).__init__(Name)
      self._ns = OrderedDict()

   def __str__(self):
      return self._name

   @ObjectAdd("namespace")
   def AddNamespace(self, Object = None):
      self._ns[Object.name] = Object

   @ObjectAdd("function")
   def AddFunctions(self, Object = None):
      self._functions[Object.name] = Object

   def FindFunction(self, Name = None):
      return self.__find(Name, "function")

   def FindNamespace(self, Name = None):
      return self.__find(Name, "namespace")

class BerylFunction(BerylObject):
   def __init__(self, Name = None):
      super(BerylFunction, self).__init__(Name)
      self._builtin = False

   def __str__(self):
      return self._name

   builtin = property(fget = lambda self: self._builtin)

class BerylVariable(BerylObject):
   def __init__(self, Value, Name = None):
      super(BerylVariable, self).__init__(Name)
      self._value = None
      self._type = None

      if isinstance(Value, (int, str, float)):
         self._value = Value
      elif isinstance(Value, BerylVariable) or issubclass(Value, BerylVariable):
         self._value = Value.value
      else:
         self.error("unknown type %s", type(value))

      if self._name and not isinstance(self._name, str):
         self.error("variable name must be a string")

      if self._value is not None:
         self._type = type(self._value)

   def __check(self, Object):
      retval = None
      if isinstance(Object, BerylVariable):
         retval = self.__check(Object.value)
      elif type(Object) == self._type:
         retval = Object
      else:
         if isinstance(self._value, (int, float, bool)):
            if isinstance(Object, (int, float, bool)):
               retval = Object
            else:
               self.error("invalid type of operand %s in line %d",
                     type(Object), self.line)
         elif isinstance(self._value, str):
            if isinstance(Object, (int, float, str)):
               retval = Object
            else:
               self.error("invalid type of operand %s in line %d",
                     type(Object), self.line)
         else:
            self.error("unknown type of operand %s in line %d",
                  type(Object), self.line)

      return retval

   def __int__(self):
      retval = None
      if isinstance(self._value, int):
         retval = self.value
      elif isinstance(self._value, (float, bool)):
         retval = int(self.value)

      return retval

   def __float__(self):
      retval = None
      if isinstance(self._value, (int, bool)):
         retval = float(self.value)
      elif isinstance(self._value, float):
         retval = self.value

      return retval

   def __hash__(self):
      return hash(self.value)

   def __str__(self):
      retval = None
      if self._name:
         retval = self._name
      else:
         retval = str(self.value)

      return retval

   def __add__(self, value):
      return BerylVariable(self.value + self.__check(value))

   @InvalidOperand(str)
   def __sub__(self, value):
      return BerylVariable(self.value - self.__check(value))

   @InvalidOperand(str)
   def __mul__(self, value):
      return BerylVariable(self.value * self.__check(value))

   @InvalidOperand(str)
   def __truediv__(self, value):
      return BerylVariable(self.value / self.__check(value))

   def __radd__(self, value):
      return BerylVariable(self.__check(value) + self.value)

   def __iadd__(self, value):
      self._value += self.__check(value)
      return self

   @InvalidOperand(str)
   def __rsub__(self, value):
      return BerylVariable(self.__check(value) - self.value)

   @InvalidOperand(str)
   def __isub__(self, value):
      self._value -= self.__check(value)
      return self

   @InvalidOperand(str)
   def __rmul__(self, value):
      return BerylVariable(self.__check(value) * self.value)

   @InvalidOperand(str)
   def __imul__(self, value):
      self._value *= self.__check(value)
      return self

   @InvalidOperand(str)
   def __rtruediv__(self, value):
      return BerylVariable(self.__check(value) / self.value)

   @InvalidOperand(str)
   def __itruediv__(self, value):
      self._value /= self.__check(value)
      return self

   @InvalidOperand(str, float)
   def __or__(self, value):
      return BerylVariable(value | self.value)

   @InvalidOperand(str, float)
   def __and__(self, value):
      return BerylVariable(value & self.value)

   @InvalidOperand(str, float)
   def __xor__(self, value):
      return BerylVariable(value ^ self.value)

   @InvalidOperand(str, float)
   def __ror__(self, value):
      return BerylVariable(self.__check(value) | self.value)

   @InvalidOperand(str, float)
   def __ior__(self, value):
      self._value |= self.__check(value)
      return self

   @InvalidOperand(str, float)
   def __rand__(self, value):
      return BerylVariable(self.__check(value) & self.value)

   @InvalidOperand(str, float)
   def __iand__(self, value):
      self._value &= self.__check(value)
      return self

   @InvalidOperand(str, float)
   def __rxor__(self, value):
      return BerylVariable(self.__check(value) ^ self.value)

   @InvalidOperand(str, float)
   def __ixor__(self, value):
      self._value ^= self.__check(value)
      return self

   @InvalidOperand(int, float, bool)
   def __getslice__(self, sidx = 0, eidx = None):
      return self.value[sidx:eidx]

   @InvalidOperand(int, float, bool)
   def __contains__(self, string = ""):
      return string in self.value

   def __lt__(self, value):
      return self._value < self.__check(value)

   def __le__(self, value):
      return self._value <= self.__check(value)

   def __eq__(self, value):
      return self._value == self.__check(value)

   def __ne__(self, value):
      return self._value != self.__check(value)

   def __gt__(self, value):
      return self._value > self.__check(value)

   def __ge__(self, value):
      return self._value >= self.__check(value)

   def __bool__(self):
      return bool(self.value)

   @InvalidOperand(int, float)
   def __len__(self):
      return len(self.value)

   value = property(fget = lambda self: self._value)

class BerylTrue(BerylVariable):
   def __init__(self):
      super(BerylTrue, self).__init__(True)

class BerylFalse(BerylVariable):
   def __init__(self):
      super(BerylFalse, self).__init__(False)

class BerylBoolean(BerylBase):
   symbol = None
   def __init__(self, tokens = None):
      super(BerylBoolean, self).__init__()

      if tokens:
         self._args = tokens
      else:
         self._args = tokens[0][0:2]

   def __str__(self):
      sep = " %s " % self.symbol
      return "(" + sep.join(map(str, self._args)) + ")"

class BerylAnd(BerylBoolean):
   symbol = '&'

   @beryl_bool
   def __bool__(self):
      retval = True
      for ix in self._args:
         if isinstance(ix, str):
            retval = eval(ix)
         else:
            retval = bool(ix)

         if not retval:
            break

      return retval

class BerylOr(BerylBoolean):
   symbol = '|'

   @beryl_bool
   def __bool__(self):
      retval = False
      for ix in self._args:
         if isinstance(ix, str):
            retval = eval(ix)
         else:
            retval = bool(ix)

         if retval:
            break

      return retval

class BerylNot(BerylBoolean):
   def __init__(self, tokens):
      super(BerylNot, self).__init__(tokens[0][1])

   def __str__(self):
      return "~" + str(self._args)

   @beryl_bool
   def __bool__(self):
      retval = None
      if isinstance(self._args, str):
         retval = eval(self._args)
      else:
         retval = bool(self._args)

      return not retval

class BerylCompare(BerylBoolean):
   def __init__(self, tokens):
      super(BerylCompare, self).__init__(tokens[0])

   @beryl_bool
   def __bool__(self):
      retval = None
      oper = self._args[1]
      if oper in ("<", "lt"):
         retval = self._args[0] < self._args[2]
      elif oper in ("<=", "le"):
         retval = self._args[0] <= self._args[2]
      elif oper in ("==", "is"):
         retval = self._args[0] == self._args[2]
      elif oper in ("!=", "ne", "<>"):
         retval = self._args[0] != self._args[2]
      elif oper in (">", "gt"):
         retval = self._args[0] > self._args[2]
      elif oper in (">=", "ge"):
         retval = self._args[0] >= self._args[2]
      else:
         self.error("invalid operator value %s", oper)

      self.symbol = oper

      return retval

class BerylMath(BerylBase):
   def __init__(self, tokens):
      self._tokens = tokens[0]

   def __call__(self, variables):
      raise NotImplementedError()

class BerylSignOp(BerylMath):
   def __init__(self, tokens):
      super(BerylSignOp, self).__init__(tokens)
      self._sign, self._value = self._tokens

   def __call__(self, variables):
      multiplier = { '+' : 1, '-' : -1 }[self._sign]
      return multiplier * self._value[variables]

class BerylMultOp(BerylMath):

